package cn.agiledata.bank.audit.service;

import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import cn.agiledata.bank.audit.dao.IAuditGroupDao;
import cn.agiledata.bank.audit.dao.IAuditOperandTypeDao;
import cn.agiledata.bank.audit.dao.IAuditRuleDao;
import cn.agiledata.bank.audit.exception.AuditException;
import cn.agiledata.bank.common.util.Constant;
import cn.agiledata.bank.hibernateBean.Account;
import cn.agiledata.bank.hibernateBean.AuditGroup;
import cn.agiledata.bank.hibernateBean.AuditOperandType;
import cn.agiledata.bank.hibernateBean.AuditRule;
import cn.agiledata.bank.hibernateBean.AuditStep;
import cn.agiledata.bank.hibernateBean.Operator;
import cn.agiledata.bank.hibernateBean.UserInfo;

/**
 * @author 马贤波
 * 
 */
public final class AuditRuleServiceImpl implements AuditRuleService {
    private IAuditRuleDao auditRuleDao;

    private IAuditGroupDao auditGroupDao;

    private IAuditOperandTypeDao auditOperandTypeDao;

    public AuditRuleServiceImpl() {
    }

    public void setAuditRuleDao(IAuditRuleDao auditRuleDao) {
        this.auditRuleDao = auditRuleDao;
    }

    public void setAuditGroupDao(IAuditGroupDao auditGroupDao) {
        this.auditGroupDao = auditGroupDao;
    }

    public void setAuditOperandTypeDao(IAuditOperandTypeDao auditOperandTypeDao) {
        this.auditOperandTypeDao = auditOperandTypeDao;
    }

    public AuditRule[] getUserAuditRuleList(long accountId,
            boolean validityRelated) {
        Integer valid = validityRelated ? new Integer(Constant.VALID_YES)
                : null;
        return this.auditRuleDao.getAuditRules(false, new Long(accountId),
                valid, Boolean.TRUE);
    }

    public AuditRule[] getSystemAuditRuleList(boolean validityRelated) {
        Integer valid = validityRelated ? new Integer(Constant.VALID_YES)
                : null;
        return this.auditRuleDao.getAuditRules(true, null, valid, Boolean.TRUE);
    }

    public AuditOperandType[] getAuditOperandTypes(boolean systemType) {
        return this.auditOperandTypeDao.getAuditOperandTypes(systemType,
                Boolean.TRUE);
    }

    public AuditRule getAuditRuleById(long id) {
        return this.auditRuleDao.getAuditRuleById(id);
    }

    private void setAuditRule(AuditRule auditRule, Account account,
            long auditOperandTypeId, String name, int valid,
            AuditStep[] auditSteps, String auditorId) {
        if (auditRule.getAccount() == null)
            auditRule.setAccount(account);
        AuditOperandType auditOperandType = this.auditOperandTypeDao
                .getAuditOperandTypeById(auditOperandTypeId);
        if (auditOperandType != null)
            auditRule.setAuditOperandType(auditOperandType);
        auditRule.setName(name);
        auditRule.setValid(valid);
        if (auditSteps != null) {
            Set auditStepSet = auditRule.getAuditSteps();
            for (Iterator it = auditStepSet.iterator(); it.hasNext();) {
                AuditStep auditStep = (AuditStep) it.next();
                auditStep.setAuditRule(null);
            }
            auditStepSet.clear();
            for (int i = 0; i < auditSteps.length; i++) {
                auditSteps[i].setId(null);
                auditSteps[i].getAuditGroup().setUsed(Constant.USED_YES);
                auditSteps[i].setAuditRule(auditRule);
                auditStepSet.add(auditSteps[i]);
            }
        }
        auditRule.setUpdateUser(auditorId);
        auditRule.setUpdateTime(new Date());
        if (auditRule.getCreateUser() == null) {
            auditRule.setCreateUser(auditRule.getUpdateUser());
            auditRule.setCreateTime(auditRule.getUpdateTime());
        }
    }

    private void resetValid(AuditRule auditRule) {
        if (Constant.VALID_YES == auditRule.getValid()) {
            Account account = auditRule.getAccount();
            Long auditRuleId = auditRule.getId();
            AuditRule validAuditRule;
            if (auditRuleId == null) {
                Long accountId = account == null ? null : account.getId();
                validAuditRule = this.auditRuleDao.getValidAuditRule(accountId,
                        auditRule.getAuditOperandType());
            } else {
                validAuditRule = this.auditRuleDao.getValidAuditRuleExceptFor(
                        account, auditRule.getAuditOperandType(), auditRuleId);
            }
            if (validAuditRule != null
                    && !validAuditRule.getId().equals(auditRule.getId())) {
                validAuditRule.setValid(Constant.VALID_NO);
                this.auditRuleDao.saveOrUpdateAuditRule(validAuditRule);
            }
        }
    }

    private void checkAuditRule(Set auditSteps, boolean systemRule,
            Account account, Long auditRuleId, String name)
            throws AuditException {
        // 审核规则必须至少包含一条审核步骤
        if ((auditRuleId != null && !auditRuleId.equals(new Long(0))) 
        		&& (auditSteps == null || auditSteps.size() == 0)) {
            throw AuditException.AUDIT_RULE_NO_STEP;
        }
        // 后台系统审核规则不允许重名
        if (systemRule) {
            AuditRule auditRule = this.auditRuleDao.getAuditRuleByName(
                    systemRule, account, name);
            if (!(auditRule == null || auditRule.getId().equals(auditRuleId))) {
                throw AuditException.AUDIT_RULE_NOT_UNIQUE_NAME;
            }
        }
    }

    private AuditRule saveOrUpdateAuditRule(AuditRule auditRule,
            boolean systemRule, Account account, long auditOperandTypeId,
            String name, int valid, AuditStep[] auditSteps, String auditorId)
            throws AuditException {
        checkAuditRule(auditRule.getAuditSteps(), systemRule, account, auditRule.getId(), name);
        setAuditRule(auditRule, account, auditOperandTypeId, name, valid,
                auditSteps, auditorId);
        resetValid(auditRule);
        this.auditRuleDao.saveOrUpdateAuditRule(auditRule);
        return auditRule;
    }

    public AuditRule addUserAuditRule(Operator operator,
            long auditOperandTypeId, String name, int valid,
            AuditStep[] auditSteps) throws AuditException {
        AuditRule auditRule = new AuditRule();
        auditRule.setUsed(Constant.USED_NO);
        return saveOrUpdateAuditRule(auditRule, false, operator.getAccount(),
                auditOperandTypeId, name, valid, auditSteps, operator.getId()
                        .toString());
    }

    public AuditRule addSystemAuditRule(UserInfo user, long auditOperandTypeId,
            String name, int valid, AuditStep[] auditSteps)
            throws AuditException {
        AuditRule auditRule = new AuditRule();
        auditRule.setUsed(Constant.USED_NO);
        return saveOrUpdateAuditRule(auditRule, true, null, auditOperandTypeId,
                name, valid, auditSteps, user.getId().toString());
    }

    public AuditRule amendUserAuditRule(long auditRuleId, Operator operator,
            long auditOperandTypeId, String name, int valid,
            AuditStep[] auditSteps) throws AuditException {
        AuditRule auditRule = this.auditRuleDao.getAuditRuleById(auditRuleId);
        Set auditStepSet = new HashSet(auditRule.getAuditSteps());
        auditRule = saveOrUpdateAuditRule(auditRule, false, operator
                .getAccount(), auditOperandTypeId, name, valid, auditSteps,
                operator.getId().toString());
        if (auditRule != null) {
            resetAuditGroupUsed(auditRule.getAccount(), auditStepSet);
        }
        return auditRule;
    }

    public AuditRule amendSystemAuditRule(long auditRuleId, UserInfo user,
            long auditOperandTypeId, String name, int valid,
            AuditStep[] auditSteps) throws AuditException {
        AuditRule auditRule = this.auditRuleDao.getAuditRuleById(auditRuleId);
        Set auditStepSet = new HashSet(auditRule.getAuditSteps());
        auditRule = saveOrUpdateAuditRule(auditRule, true, null,
                auditOperandTypeId, name, valid, auditSteps, user.getId()
                        .toString());
        if (auditRule != null) {
            resetAuditGroupUsed(auditRule.getAccount(), auditStepSet);
        }
        return auditRule;
    }

    public boolean removeAuditRule(long auditRuleId) {
        AuditRule auditRule = this.auditRuleDao.getAuditRuleById(auditRuleId);
        if (auditRule.getUsed() == Constant.USED_YES)
            return false;
        Set auditSteps = new HashSet(auditRule.getAuditSteps());
        this.auditRuleDao.deleteAuditRule(auditRule);
        resetAuditGroupUsed(auditRule.getAccount(), auditSteps);
        return true;
    }

    public void amendAuditRuleValidity(long auditRuleId, boolean valid,
            Operator operator) throws AuditException {
        AuditRule auditRule = this.auditRuleDao.getAuditRuleById(auditRuleId);
        if (auditRule.isSystemRule()) {
            throw AuditException.CANNOT_AUDIT_SYSTEM_TYPE;
        }
        long auditOperandTypeId = auditRule.getAuditOperandType().getId()
                .longValue();
        int validValue = valid ? Constant.VALID_YES : Constant.VALID_NO;
        saveOrUpdateAuditRule(auditRule, false, operator.getAccount(),
                auditOperandTypeId, auditRule.getName(), validValue,
                (AuditStep[]) null, operator.getId().toString());
    }

    public void amendAuditRuleValidity(long auditRuleId, boolean valid,
            UserInfo user) throws AuditException {
        AuditRule auditRule = this.auditRuleDao.getAuditRuleById(auditRuleId);
        if (!auditRule.isSystemRule()) {
            throw AuditException.CANNOT_AUDIT_CORP_TYPE;
        }
        long auditOperandTypeId = auditRule.getAuditOperandType().getId()
                .longValue();
        int validValue = valid ? Constant.VALID_YES : Constant.VALID_NO;
        saveOrUpdateAuditRule(auditRule, true, null, auditOperandTypeId,
                auditRule.getName(), validValue, (AuditStep[]) null, user
                        .getId().toString());
    }

    private void resetAuditGroupUsed(Account account, Set auditSteps) {
        for (Iterator it = auditSteps.iterator(); it.hasNext();) {
            AuditStep auditStep = (AuditStep) it.next();
            AuditGroup auditGroup = auditStep.getAuditGroup();
            long auditGroupId = auditGroup.getId().longValue();
            if (!isActuallyUsedAuditGroup(account, auditGroupId)) {
                auditGroup.setUsed(Constant.USED_NO);
                this.auditGroupDao.saveOrUpdateAuditGroup(auditGroup);
            }
        }
    }

    private boolean isActuallyUsedAuditGroup(Account account, long auditGroupId) {
        AuditRule[] auditRules;
        if (account != null) {
            long accountId = account.getId().longValue();
            auditRules = getUserAuditRuleList(accountId, false);
        } else {
            auditRules = getSystemAuditRuleList(false);
        }
        for (int i = 0; i < auditRules.length; i++) {
            Set auditSteps = auditRules[i].getAuditSteps();
            for (Iterator it = auditSteps.iterator(); it.hasNext();) {
                AuditStep auditStep = (AuditStep) it.next();
                if (auditStep.getAuditGroup().getId().longValue() == auditGroupId) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean containsOperator(long accountId, long operatorId,
            boolean validityRelated) {
        AuditRule[] auditRules = getUserAuditRuleList(accountId,
                validityRelated);
        for (int i = 0; i < auditRules.length; i++) {
            for (Iterator it = auditRules[i].getAuditSteps().iterator(); it
                    .hasNext();) {
                AuditStep auditStep = (AuditStep) it.next();
                Set operators = auditStep.getAuditGroup().getOperators();
                for (Iterator iterator = operators.iterator(); iterator
                        .hasNext();) {
                    Operator operator = (Operator) iterator.next();
                    if (operatorId == operator.getId().longValue()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public boolean containsUser(long userId, boolean validityRelated) {
        AuditRule[] auditRules = getSystemAuditRuleList(validityRelated);
        for (int i = 0; i < auditRules.length; i++) {
            for (Iterator it = auditRules[i].getAuditSteps().iterator(); it
                    .hasNext();) {
                AuditStep auditStep = (AuditStep) it.next();
                Set users = auditStep.getAuditGroup().getUsers();
                for (Iterator iterator = users.iterator(); iterator.hasNext();) {
                    UserInfo user = (UserInfo) iterator.next();
                    if (userId == user.getId().longValue()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}
